﻿using System;
using System.Collections.Generic;
using Didaku.QEngine.Common.Abstracts.Entities;
using Didaku.QEngine.Common.Entities;
using Didaku.QEngine.Common.EventArg;
using Didaku.QEngine.Common.Interfaces.Engine;
using Didaku.QEngine.Common.Wrapper.Engine;
using NLog;

namespace Didaku.QEngine
{
    /// <summary>排队系统队列模型引擎
    /// </summary>
    public class Engine : IQueueEngine
    {
        private static readonly Logger _Logger = LogManager.GetCurrentClassLogger();

        static Engine()
        {
            _Logger.Debug(string.Format("Static Load {0}...", typeof (Engine).FullName));
        }

        #region 成员变量

        /// <summary>该引擎对应的柜台逻辑的集合的备份（当需要调整柜台之间的逻辑时使用）
        /// </summary>
        private readonly IDictionary<string, ServiceLogic> _LogicsSource = new Dictionary<string, ServiceLogic>();

        /// <summary>持久化控制器
        /// </summary>
        private readonly Persistence _Persistence = new Persistence();

        /// <summary>卡片信息存储服务器
        /// </summary>
        private ICardService _CardService;

        /// <summary>统计数量服务器
        /// </summary>
        private ICountService _CountService;

        /// <summary>该引擎对应的柜台逻辑的集合
        /// </summary>
        private IDictionary<string, ServiceLogic> _Logics;

        /// <summary>该引擎对应的所有队列的集合
        /// </summary>
        private IDictionary<string, ServiceQueue> _Queues;

        internal IDictionary<string, ServiceQueue> Queues { get { return _Queues; } } 

        #endregion

        #region Implementation of IQueueEngine

        /// <summary>统计人数服务
        /// </summary>
        public ICountService CountService
        {
            get { return _CountService; }
        }

        /// <summary>卡片信息存储服务器
        /// </summary>
        public ICardService CardService
        {
            get { return _CardService; }
        }

        /// <summary>初始化
        /// </summary>
        /// <param name="department">网点基础信息.</param>
        /// <returns></returns>
        public bool Initialize(DepartmentInfo department)
        {
            try
            {
                //初始化队列相关,将从配置中克隆出一套
                InitializeQueues(department);
                //初始化逻辑相关,将从配置中克隆出一套
                InitializeLogics(department);
                //初始化卡片信息存储服务器
                InitializeCardService();
                //初始化统计人数服务
                InitializeCountService();
                //恢复当天的Transaction
                InitializeTransactions();
                _Logger.Info("引擎初始化完成.");
                return true;
            }
            catch (Exception e)
            {
                _Logger.WarnException("引擎初始化异常.", e);
                return false;
            }
        }

        /// <summary>根据指定的队列生成交易
        /// </summary>
        /// <param name="transaction">输出:生成的交易.</param>
        /// <param name="serviceQueueId">指定的队列的id.</param>
        /// <returns>是否生成成功，当未成功时，交易为Null</returns>
        public bool Generate(out Transaction transaction, string serviceQueueId)
        {
            try
            {
                int currCount;
                transaction = _Queues[serviceQueueId].Generate(out currCount);
                _Persistence.Backup(serviceQueueId, currCount);
                return true;
            }
            catch (Exception e)
            {
                _Logger.WarnException(string.Format("指定队列生成票号异常。{0}", serviceQueueId), e);
                transaction = null;
                return false;
            }
        }

        /// <summary>根据指定的呼叫方法获取交易
        /// </summary>
        /// <param name="transaction">输出:获取的交易.</param>
        /// <param name="callFunc">指定的呼叫方法函数.</param>
        /// <returns>是否获取成功，当未成功时，交易为Null</returns>
        public bool Assign<T>(out Transaction transaction, IAssignFunc<T> callFunc)
        {
            try
            {
                ServiceLogic logic = _Logics[callFunc.Counter.Id];
                if (callFunc.Execute(out transaction, logic, _Queues.Values))
                {
                    transaction.SetOnServe(callFunc.Counter.Index, callFunc.Staff.Id);
                    return true;
                }
                transaction = null;
                return false;
            }
            catch (Exception e)
            {
                _Logger.WarnException(string.Format("柜台呼叫异常。{0}", callFunc.Counter.Name), e);
                transaction = null;
                return false;
            }
        }

        /// <summary>修改指定的交易的相关属性。如:弃号，插前，延后
        /// </summary>
        /// <param name="modifyFunc">指定的修改方法.</param>
        /// <returns>是否生成成功，当未成功时，交易为Null</returns>
        public bool Modify<T>(IModifyFunc<T> modifyFunc)
        {
            return modifyFunc.Execute(_Queues.Values);
        }

        /// <summary>调整指定的交易与队列之间关系。如转移交易到另外的队列。
        /// </summary>
        /// <param name="adjustFunc">调整的方法函数</param>
        /// <returns>是否调整成功</returns>
        public bool Adjust<T>(IAdjustFunc<T> adjustFunc)
        {
            return adjustFunc.Execute(_Logics);
        }

        /// <summary>根据指定的删除筛选条件从队列中移除交易
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="deleteFunc">删除的方法函数</param>
        /// <returns>是否删除成功</returns>
        public bool Delete<T>(IDeleteFunc<T> deleteFunc)
        {
            return deleteFunc.Execute();
        }

        /// <summary>调整柜台间的逻辑（合岗，换岗等）
        /// </summary>
        /// <param name="changeFunc">调整的方法函数</param>
        /// <returns>是否调整成功</returns>
        public bool ChangeLogic<T>(IChangeLogicFunc<T> changeFunc)
        {
            return changeFunc.Execute(_LogicsSource, _Logics);
        }

        #endregion

        #region 初始化相关

        /// <summary>初始化队列相关
        /// </summary>
        private void InitializeQueues(DepartmentInfo department)
        {
            _Logger.Debug("初始化队列相关,将从配置中克隆出一套");
            var srcQs = department.ServiceQueueMap;
            _Queues = new Dictionary<string, ServiceQueue>(srcQs.Count);
            foreach (var pair in srcQs)
            {
                ServiceQueue cloneQueue = pair.Value.Clone(false); //克隆一份队列，防止外界破坏队列
                cloneQueue.TransactionAddedEvent += TransactionAddedEvent;
                cloneQueue.TransactionRemovedEvent += TransactionRemovedEvent;
                cloneQueue.TransactionChangedEvent += TransactionChangedEvent;
                _Queues.Add(pair.Key, cloneQueue);
            }
        }

        /// <summary>初始化逻辑相关
        /// </summary>
        private void InitializeLogics(DepartmentInfo department)
        {
            _Logger.Debug("初始化逻辑相关");
            var srcLogics = department.ServiceLogicMap;
            _Logics = new Dictionary<string, ServiceLogic>(srcLogics.Count);
            //制作一份柜台逻辑的克隆, 以备合并柜台逻辑或交换柜台逻辑时发生引用的变化
            foreach (var pair in srcLogics)
            {
                ServiceLogic srcLogic = pair.Value;
                ServiceLogic cloneLogic = srcLogic.Clone(false); //浅克隆一份Logic
                foreach (ServiceQueueGroup srcGroup in srcLogic)
                {
                    var cloneGroup = (ServiceQueueGroup) srcGroup.Clone(); //队列组也进行克隆,但事实上组中的队列未被克隆,仍是引用的复制
                    foreach (ServiceQueue innerQueue in cloneGroup)
                    {
                        if (!_Queues.ContainsKey(innerQueue.Id))
                            _Queues.Add(innerQueue.Id, innerQueue); //一些自动创建的队列，如:超级队列等
                    }

                    cloneLogic.Add(cloneGroup);
                }
                _Logics.Add(pair.Key, cloneLogic);
            }
            //制作一份逻辑的备份（引用）
            foreach (var logic in _Logics)
            {
                _LogicsSource.Add(logic.Key, logic.Value);
            }
        }

        /// <summary>初始化卡片信息存储服务器
        /// </summary>
        private void InitializeCardService()
        {
            _Logger.Debug("初始化卡片信息存储服务");
            //卡片信息存储服务器
            _CardService = new CardService();
        }

        /// <summary>初始化统计人数服务
        /// </summary>
        private void InitializeCountService()
        {
            _Logger.Debug("初始化统计人数服务");
            //统计人数服务
            _CountService = new CountService(_Logics, _Queues);
        }

        /// <summary>恢复当天的Transaction
        /// </summary>
        private void InitializeTransactions()
        {
            _Logger.Debug("恢复当天的Transaction等数据");
            foreach (var pair in _Queues)
            {
                Transaction[] trans = _Persistence.Select(pair.Key);
                if (trans != null && trans.Length > 0)
                {
                    foreach (Transaction transaction in trans)
                        pair.Value.Add(transaction, false); //这样向队列恢复数据时，不激发事件
                }
            }
            // 队列初始化完成后，启动统计服务
            _CountService.Start(_Queues.Values);
        }

        #endregion

        #region 事件相关

        protected void TransactionChangedEvent(object sender, ServiceQueueEventArgs e)
        {
            _Persistence.Update(e.Transaction);
        }

        protected void TransactionRemovedEvent(object sender, ServiceQueueEventArgs e)
        {
        }

        protected void TransactionAddedEvent(object sender, ServiceQueueEventArgs e)
        {
            _Persistence.Add(e.Transaction);
        }

        #endregion
    }
}